package com.github.tangyi.user.service;

import cn.hutool.core.collection.CollUtil;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.properties.SysProperties;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.utils.TreeUtil;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.user.api.constant.MenuConstant;
import com.github.tangyi.user.api.dto.MenuDto;
import com.github.tangyi.user.api.module.Menu;
import com.github.tangyi.user.api.module.Role;
import com.github.tangyi.user.api.module.RoleMenu;
import com.github.tangyi.user.mapper.MenuMapper;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 菜单service
 *
 * @author tangyi
 * @date 2018/8/26 22:45
 */
@AllArgsConstructor
@Service
public class MenuService extends CrudService<MenuMapper, Menu> {

    private final MenuMapper menuMapper;

    private final RoleMenuService roleMenuService;

    private final SysProperties sysProperties;

    /**
     * 返回当前用户的树形菜单集合
     *
     * @return List
     * @author tangyi
     * @date 2019-09-14 14:41
     */
    public List<MenuDto> findUserMenu() {
        List<MenuDto> menuDtoList = new ArrayList<>();
        String tenantCode = SysUtil.getTenantCode(), identifier = SysUtil.getUser();
        List<Menu> userMenus;
        // 查询默认租户的菜单
        Menu condition = new Menu();
        condition.setTenantCode(SecurityConstant.DEFAULT_TENANT_CODE);
        condition.setApplicationCode(SysUtil.getSysCode());
        condition.setType(MenuConstant.MENU_TYPE_MENU);
        List<Menu> defaultMenus = findAllList(condition);

        // 超级管理员
        if (identifier.equals(sysProperties.getAdminUser())) {
            // 获取租户的菜单和默认租户的菜单，最后组装数据，租户的菜单优先
            if (SecurityConstant.DEFAULT_TENANT_CODE.equals(tenantCode)) {
                userMenus = defaultMenus;
            } else {
                // 获取角色的菜单
                condition.setTenantCode(tenantCode);
                condition.setApplicationCode(SysUtil.getSysCode());
                condition.setType(MenuConstant.MENU_TYPE_MENU);
                List<Menu> tenantMenus = findAllList(condition);
                // 组装数据
                userMenus = mergeMenu(defaultMenus, tenantMenus);
            }
        } else {
            List<Role> roleList = SecurityUtil.getCurrentAuthentication().getAuthorities().stream()
                    // 按角色过滤
                    .filter(authority -> authority.getAuthority() != null && authority.getAuthority()
                            .startsWith("role_")).map(authority -> {
                        Role role = new Role();
                        role.setRoleCode(authority.getAuthority());
                        return role;
                    }).collect(Collectors.toList());
            // 根据角色code批量查找菜单
            List<Menu> tenantMenus = finMenuByRoleList(roleList, tenantCode);
            // 组装数据
            userMenus = mergeMenu(defaultMenus, tenantMenus);
        }
        if (CollectionUtils.isNotEmpty(userMenus)) {
            userMenus.stream()
                    // 菜单类型
                    .filter(menu -> MenuConstant.MENU_TYPE_MENU.equals(menu.getType()))
                    // dto封装
                    .map(MenuDto::new)
                    // 去重
                    .distinct().forEach(menuDtoList::add);
            // 排序、构建树形关系
            return TreeUtil.buildTree(CollUtil.sort(menuDtoList, Comparator.comparingInt(MenuDto::getSort)), CommonConstant.ROOT);
        }
        return Lists.newArrayList();
    }

    /**
     * 根据角色查找菜单
     *
     * @param role       角色标识
     * @param tenantCode 租户标识
     * @return List
     * @author tangyi
     * @date 2018/8/27 16:00
     */
    @Cacheable(value = "menu#" + CommonConstant.CACHE_EXPIRE, key = "#role")
    public List<Menu> findMenuByRole(String role, String tenantCode) {
        List<Menu> menus = new ArrayList<>();
        // 返回默认租户的角色菜单
        if (!tenantCode.equals(SecurityConstant.DEFAULT_TENANT_CODE))
            menus = menuMapper.findByRole(role, SecurityConstant.DEFAULT_TENANT_CODE);
        List<Menu> tenantMenus = menuMapper.findByRole(role, tenantCode);
        if (CollectionUtils.isNotEmpty(tenantMenus))
            menus.addAll(tenantMenus);
        return menus;
    }

    /**
     * 批量查询菜单
     *
     * @param roleList   roleList
     * @param tenantCode tenantCode
     * @return List
     * @author tangyi
     * @date 2019/07/03 23:50:35
     */
    private List<Menu> finMenuByRoleList(List<Role> roleList, String tenantCode) {
        List<Menu> menus = Lists.newArrayList();
        for (Role role : roleList) {
            List<Menu> roleMenus = this.findMenuByRole(role.getRoleCode(), tenantCode);
            if (CollectionUtils.isNotEmpty(roleMenus))
                menus.addAll(roleMenus);
        }
        return menus;
    }

    /**
     * 查询全部菜单，包括租户本身的菜单和默认租户的菜单
     *
     * @param menu menu
     * @return List
     * @author tangyi
     * @date 2019/04/10 17:58
     */
    @Override
    public List<Menu> findAllList(Menu menu) {
        List<Menu> menus = new ArrayList<>();
        if (!menu.getTenantCode().equals(SecurityConstant.DEFAULT_TENANT_CODE)) {
            Menu defaultMenu = new Menu();
            defaultMenu.setApplicationCode(SysUtil.getSysCode());
            defaultMenu.setTenantCode(SecurityConstant.DEFAULT_TENANT_CODE);
            menus = menuMapper.findAllList(defaultMenu);
        }
        List<Menu> tenantMenus = menuMapper.findAllList(menu);
        if (CollectionUtils.isNotEmpty(tenantMenus))
            menus = mergeMenu(menus, tenantMenus);
        return menus;
    }

    /**
     * 新增菜单
     *
     * @param menu menu
     * @return int
     * @author tangyi
     * @date 2018/10/28 15:56
     */
    @Transactional
    @Override
    @CacheEvict(value = {"menu", "user"}, allEntries = true)
    public int insert(Menu menu) {
        return super.insert(menu);
    }

    /**
     * 更新菜单，区分租户本身的菜单和默认租户的菜单
     *
     * @param menu menu
     * @return int
     * @author tangyi
     * @date 2018/10/30 20:19
     */
    @Transactional
    @Override
    @CacheEvict(value = {"menu", "user"}, allEntries = true)
    public int update(Menu menu) {
        String userCode = SysUtil.getUser();
        String sysCode = SysUtil.getSysCode();
        String tenantCode = SysUtil.getTenantCode();
        menu = this.get(menu);
        // 默认租户的用户更新菜单或更新本租户的菜单，直接更新
        if (tenantCode.equals(SecurityConstant.DEFAULT_TENANT_CODE) || tenantCode.equals(menu.getTenantCode())) {
            return super.update(menu);
        } else {
            // 其它租户更新默认租户的菜单，copy一份原始菜单的数据
            Long originalId = menu.getId();
            String originalTenantCode = menu.getTenantCode();
            // 重新初始化ID
            menu.setId(null);
            menu.setCommonValue(userCode, sysCode, tenantCode);
            this.insert(menu);
            // copy子菜单
            Long newMenuId = menu.getId();
            Menu condition = new Menu();
            condition.setParentId(originalId);
            condition.setTenantCode(originalTenantCode);
            // 查询子菜单
            List<Menu> childrenMenus = findList(condition);
            // 子菜单ID列表
            List<Long> childrenMenuIds = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(childrenMenus)) {
                childrenMenus.forEach(children -> {
                    childrenMenuIds.add(children.getId());
                    // 重新初始化ID
                    children.setId(null);
                    children.setCommonValue(userCode, sysCode, tenantCode);
                    // 重新绑定父菜单
                    children.setParentId(newMenuId);
                });
                // 批量插入
                insertBatch(childrenMenus);
            }
            // 更新权限数据roleMenu
            updateRoleMenu(originalId, childrenMenuIds, userCode, sysCode, tenantCode);
        }
        return super.update(menu);
    }

    /**
     * 删除菜单
     *
     * @param menu menu
     * @return int
     * @author tangyi
     * @date 2018/8/27 16:22
     */
    @Override
    @Transactional
    @CacheEvict(value = {"menu", "user"}, allEntries = true)
    public int delete(Menu menu) {
        // 删除当前菜单
        super.delete(menu);
        // 删除父节点为当前节点的菜单
        Menu parentMenu = new Menu();
        parentMenu.setParentId(menu.getId());
        parentMenu.setNewRecord(false);
        parentMenu.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
        parentMenu.setDelFlag(CommonConstant.DEL_FLAG_DEL);
        return super.update(parentMenu);
    }

    /**
     * 批量插入
     *
     * @param menus menus
     * @return int
     * @author tangyi
     * @date 2019-09-03 12:19
     */
    @Transactional
    public int insertBatch(List<Menu> menus) {
        return dao.insertBatch(menus);
    }

    /**
     * 合并默认租户和租户的菜单，租户菜单优先
     *
     * @param defaultMenus defaultMenus
     * @param tenantMenus  tenantMenus
     * @return List
     * @author tangyi
     * @date 2019-09-14 14:45
     */
    private List<Menu> mergeMenu(List<Menu> defaultMenus, List<Menu> tenantMenus) {
        if (CollectionUtils.isEmpty(tenantMenus))
            return defaultMenus;
        List<Menu> userMenus = new ArrayList<>();
        // 默认菜单
        defaultMenus.forEach(defaultMenu -> {
            Optional<Menu> menu = tenantMenus.stream()
                    .filter(tenantMenu -> tenantMenu.getName().equals(defaultMenu.getName())).findFirst();
            if (menu.isPresent()) {
                userMenus.add(menu.get());
            } else {
                userMenus.add(defaultMenu);
            }
        });
        // 租户菜单
        tenantMenus.forEach(tenantMenu -> {
            Optional<Menu> exist = userMenus.stream()
                    .filter(userMenu -> userMenu.getName().equals(tenantMenu.getName()) && userMenu.getParentId().equals(tenantMenu.getParentId())).findFirst();
            if (!exist.isPresent()) {
                userMenus.add(tenantMenu);
            }
        });
        return userMenus;
    }

    /**
     * 更新权限数据
     *
     * @param menuId          menuId
     * @param childrenMenuIds childrenMenuIds
     * @param userCode        userCode
     * @param sysCode         sysCode
     * @param tenantCode      tenantCode
     * @return
     * @author tangyi
     * @date 2019-09-14 15:57
     */
    @Transactional
    public void updateRoleMenu(Long menuId, List<Long> childrenMenuIds, String userCode, String sysCode,
                               String tenantCode) {
        List<RoleMenu> condition = new ArrayList<>();
        RoleMenu roleMenu = new RoleMenu();
        roleMenu.setMenuId(menuId);
        // 主菜单
        condition.add(roleMenu);
        // 子菜单
        if (CollectionUtils.isNotEmpty(childrenMenuIds)) {
            childrenMenuIds.forEach(childrenMenuId -> {
                RoleMenu childRoleMenu = new RoleMenu();
                childRoleMenu.setMenuId(childrenMenuId);
                condition.add(childRoleMenu);
            });
        }
        // 查询
        List<RoleMenu> roleMenus = roleMenuService.getByMenuIds(condition);
        if (CollectionUtils.isNotEmpty(roleMenus)) {
            roleMenus.forEach(tempRoleMenu -> {
                // 重新初始化ID
                tempRoleMenu.setId(null);
                tempRoleMenu.setCommonValue(userCode, sysCode, tenantCode);
            });
            // 批量插入
            roleMenuService.insertBatch(roleMenus);
        }
    }

    /**
     * 根据租户code删除
     * @param menu menu
     * @return int
     */
    @Transactional
    public int deleteByTenantCode(Menu menu) {
        return this.dao.deleteByTenantCode(menu);
    }
}
